<template>
  <div class="main-content">
    <div class="main-left" ref="left">
      <div class="canvas" ref="canvas"></div>
    </div>
    <div class="main-right">
      <div class="pannel-opt">
        <el-button type="primary" size="mini" @click="doEvent('add')">添加事件</el-button>
      </div>
      <div class="pannel-event" v-show="showEvent">
        <div class="pannel-title">
          <div class="title-left">{{ curEvent.title }}事件</div>
          <div class="btn-right" v-if="curEvent.title === '编辑'">
            <el-button type="text" size="mini" @click="doCondition('add')">添加条件</el-button>
            <el-button type="text" size="mini" @click="removeEvent">移除事件</el-button>
          </div>
        </div>
        <div class="pannel-form">
          <el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
            <el-form-item label="事件名称：" prop="name">
              <el-input v-model="ruleForm.name" maxLength="20"></el-input>
            </el-form-item>
            <el-form-item label="备注：" prop="remark">
              <el-input type="textarea" v-model="ruleForm.remark" maxLength="64"></el-input>
            </el-form-item>
            <el-form-item>
              <el-button @click="resetEvent('ruleForm')" size="medium">取消</el-button>
              <el-button type="primary" v-if="curEvent.title === '编辑'" @click="editEvent('ruleForm')" size="medium">确定</el-button>
              <el-button type="primary" v-else @click="addEvent('ruleForm')" size="medium">确定</el-button>
            </el-form-item>
          </el-form>
        </div>
      </div>
      <div class="pannel-event" v-show="showCondition">
        <div class="pannel-title">
          <div class="title-left">{{ curCondition.title }}条件</div>
          <div class="btn-right" v-if="curCondition.title === '编辑'">
            <el-button type="text" size="mini" @click="doConditionChild('add')">添加条件</el-button>
            <el-button type="text" size="mini" @click="removeCondition">移除条件</el-button>
          </div>
        </div>
        <div class="pannel-form">
          <el-form :model="conForm" :rules="conRules" ref="conForm" label-width="100px" class="demo-ruleForm">
            <el-form-item label="关系：" prop="relation">
              <el-select v-model="conForm.relation" placeholder="请选择关系" style="width:100%;" clearable>
                <el-option v-for="item in relationList" :key="item.value" :label="item.label" :value="item.value"></el-option>
              </el-select>
            </el-form-item>
            <el-form-item label="字段：" prop="field">
              <el-select v-model="conForm.field" placeholder="请选择字段" style="width:100%;" clearable>
                <el-option v-for="item in fieldList" :key="item.value" :label="item.label" :value="item.value"></el-option>
              </el-select>
            </el-form-item>
            <el-form-item label="条件类型：" prop="type">
              <el-select v-model="conForm.type" placeholder="请选择条件类型" style="width:100%;" @change="changeConSelect" clearable>
                <el-option v-for="item in conditionTypes" :key="item.type" :label="item.name" :value="item.type"></el-option>
              </el-select>
            </el-form-item>
            <template v-for="(item, index) in Object.keys(conForm.info)">
              <el-form-item v-if="getNameByOptions(item)" :label="getNameByKey(item)" :prop="`info.${item}`" :key="index">
                <el-select v-model="conForm.info[item]" :placeholder="`请选择${getNameByKey(item)}`" style="width:100%;">
                  <el-option v-for="(ele, key) in getNameByOptions(item)" :key="key" :label="ele.label" :value="ele.value"></el-option>
                </el-select>
              </el-form-item>
              <el-form-item v-else :label="getNameByKey(item)" :prop="`info.${item}`" :key="index">
                <el-input v-model="conForm.info[item]" maxLength="20"></el-input>
              </el-form-item>
            </template>
            <el-form-item>
              <el-button @click="resetCon('conForm')" size="medium">取消</el-button>
              <el-button type="primary" v-if="curCondition.title === '编辑'" @click="editCon('conForm')" size="medium">确定</el-button>
              <el-button type="primary" v-else @click="addCon('conForm')" size="medium">确定</el-button>
            </el-form-item>
          </el-form>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import {dia, layout, shapes} from 'jointjs'
import dagre from 'dagre'
import data from './data'
import graphlib from '@dagrejs/graphlib'

export default {
  name: 'AlarmCondition',
  components: {},
  data() {
    return {
      ruleForm: {
        name: '',
        remark: ''
      },
      rules: {
        name: [
          { required: true, message: '请输入事件名称', trigger: 'blur' }
        ]
      },
      showEvent: false,
      curEvent: {},
      showCondition: false,
      curCondition: {},
      conForm: {
        relation: '',
        field: '',
        type: '',
        info: {}
      },
      conRules: {},
      relationList: [
        { label: '与', value: 'and' },
        { label: '或', value: 'or' },
        { label: '非', value: 'not' }
      ],
      fieldList: [
        { label: '条件1', value: '条件1' },
        { label: '条件2', value: '条件2' },
        { label: '条件3', value: '条件3' }
      ],
      conditionTypes: [
        {
          type: 'value',
          name: '条件属性',
          props: [
            {
              label: '属性',
              field: 'field',
              readonly: true
            },
            {
              label: '操作符',
              field: 'operator',
              options: [
                { label: '=', value: 'eq' },
                { label: '>', value: 'gt' },
                { label: '<', value: 'lt' },
                { label: '>=', value: 'gte' },
                { label: '<=', value: 'lte' },
                { label: '!=', value: 'ne' }
              ]
            },
            {
              label: '值',
              field: 'value'
            }
          ]
        },
        {
          type: 'resource',
          name: '引用资源',
          props: [
            {
              label: '资源',
              field: 'value',
              options: [
                { label: '资源1', value: '1' },
                { label: '资源2', value: '2' },
                { label: '资源3', value: '3' }
              ]
            }
          ]
        },
        {
          type: 'asset_type',
          name: '引用资产分类',
          props: [
            {
              label: '资产分类',
              field: 'value',
              options: [
                { label: '资产分类1', value: '1' },
                { label: '资产分类2', value: '2' },
                { label: '资产分类3', value: '3' }
              ]
            }
          ]
        },
        {
          type: 'filter',
          name: '引用过滤器',
          props: [
            {
              label: '过滤器',
              field: 'value',
              options: [
                { label: '过滤器1', value: '1' },
                { label: '过滤器2', value: '2' },
                { label: '过滤器3', value: '3' }
              ]
            }
          ]
        },
        {
          type: 'rule',
          name: '引用规则',
          props: [
            {
              label: '规则',
              field: 'value',
              options: [
                { label: '与', value: 'and' },
                { label: '或', value: 'or' },
                { label: '非', value: 'not' }
              ]
            }
          ]
        },
        {
          type: 'asset_prop',
          name: '引用资产属性',
          props: [
            {
              label: '属性',
              field: 'field',
              readonly: true
            },
            {
              label: '操作符',
              field: 'operator',
              options: [
                { label: '=', value: 'eq' },
                { label: '>', value: 'gt' },
                { label: '<', value: 'lt' },
                { label: '>=', value: 'gte' },
                { label: '<=', value: 'lte' },
                { label: '!=', value: 'ne' }
              ]
            },
            {
              label: '值',
              field: 'value'
            }
          ]
        }
      ],
      changeList: [],
      temp: [],
      graph: null,
      paper: null,
      items: data,
      nodeSet: {},
      curNode: {} // 当前节点
    }
  },
  mounted() {
    this.init()
    this.render()
  },
  methods: {
    // 条件类型改变触发
    changeConSelect(val) {
      this.conForm.info = {}
      if (val) {
        this.conditionTypes.forEach(item => {
          if (item.type === val) {
            this.changeList = item.props
          }
        })
        this.changeList.forEach(item => {
          this.$set(this.conForm.info, item.field, '')
        })
      } else {
        this.changeList = []
      }
    },
    // 根据 key 获取 label
    getNameByKey (key) {
      if (this.changeList) {
        return this.changeList.find(item => item.field === key).label
      }
    },
    // 根据 key 获取 options
    getNameByOptions (key) {
      if (this.changeList) {
        return this.changeList.find(item => item.field === key).options
      }
    },
    // 事件相关
    doEvent(type, item) {
      this.showEvent = true
      this.curEvent.title = type === 'add' ? '添加' : '编辑'
      if (item) {
        this.ruleForm.name = item.name
      }
    },
    // 添加事件
    addEvent(formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
          this.items.push({
            id: new Date().getTime(),
            type: 'start',
            name: this.ruleForm.name,
            prev: [],
            next: []
          })
          this.init()
          this.render()
          this.resetEvent(formName)
        } else {
          return false
        }
      })
    },
    editEvent(formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
          this.items.forEach((item, index) => {
            if (item === this.curNode) {
              this.$set(this.items[index], 'name', this.ruleForm.name)
            }
          })
          this.init()
          this.render()
          this.resetEvent(formName)
        } else {
          return false
        }
      })
    },
    recursion(nodes, id) {
      // 拿到当前元素
      const curNode = nodes.filter(item => item.id === id)[0]
      if (curNode) {
        // 删除当前元素
        nodes.splice(nodes.findIndex(ele => ele.id === id), 1)
        if (curNode.next.length !== 0) {
          curNode.next.forEach(item => this.recursion(nodes, item))
        }
      }
    },
    removeEvent() {
      this.$confirm(`确认删除 ${this.curNode.name}?`, '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        // 确认删除操作
        this.showEvent = false
        // 分开始事件和结束事件
        if (this.curNode.type === 'start') {
          this.recursion(this.items, this.curNode.id)
        } else {
          this.items.forEach((item, index) => {
            if (item.next.length > 0 && item.next.includes(this.curNode.id)) {
              item.next.splice(item.next.findIndex(ele => ele === this.curNode.id), 1)
              this.$set(this.items[index], 'next', item.next)
            }
          })
          this.items.splice(this.items.findIndex(ele => ele === this.curNode), 1)
        }
        this.init()
        this.render()
      }).catch(() => {})
    },
    resetEvent(formName) {
      this.$refs[formName].resetFields()
      this.showEvent = false
      this.showCondition = false
      this.$refs.conForm.resetFields()
    },
    // 条件相关
    doCondition(type, item) {
      this.showCondition = true
      this.curCondition.title = type === 'add' ? '添加' : '编辑'
      if (item) {
        this.conForm.field = item.name
      }
    },
    // 触发条件中添加条件面板
    doConditionChild(type) {
      this.$nextTick(() => {
        this.showCondition = true
        this.curCondition.title = type === 'add' ? '添加' : '编辑'
        this.conForm.field = ''
      })
    },
    addCon(formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
          let id = new Date().getTime()
          this.items.push({
            id,
            type: 'field',
            name: this.conForm.field,
            prev: [this.curNode.id],
            next: []
          })
          this.items.forEach((item, index) => {
            if (item === this.curNode) {
              this.$set(this.items[index], 'next', [...this.items[index].next, id])
              this.init()
              this.render()
              this.resetEvent(formName)
            }
          })
        } else {
          return false
        }
      })
    },
    editCon(formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
          this.items.forEach((item, index) => {
            if (item === this.curNode) {
              this.$set(this.items[index], 'name', this.conForm.field)
            }
          })
          this.init()
          this.render()
          this.resetCon(formName)
        } else {
          return false
        }
      })
    },
    resetCon(formName) {
      this.$refs[formName].resetFields()
      this.changeConSelect()
      this.showCondition = false
    },
    removeCondition() {
      this.$confirm(`确认删除 ${this.curNode.name}?`, '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        // 确认删除操作
        this.showCondition = false
        this.items.forEach((item, index) => {
          if (item.prev.length > 0 && item.prev.includes(this.curNode.id)) {
            item.prev.splice(item.prev.findIndex(ele => ele === this.curNode.id), 1)
            this.$set(this.items[index], 'prev', item.prev)
          }
          if (item.next.length > 0 && item.next.includes(this.curNode.id)) {
            item.next.splice(item.next.findIndex(ele => ele === this.curNode.id), 1)
            this.$set(this.items[index], 'next', item.next)
          }
        })
        this.items.splice(this.items.findIndex(ele => ele === this.curNode), 1)
        this.init()
        this.render()
      }).catch(() => {})
    },
    init() {
      const canvasRect = this.$refs.canvas.getClientRects()[0]
      this.graph = new dia.Graph()
      this.paper = new dia.Paper({
        el: this.$refs.canvas,
        model: this.graph,
        width: canvasRect.width,
        height: canvasRect.height,
        gridSize: 1
      })
      this.paper.on('element:pointerclick', (cell) => {
        Object.keys(this.nodeSet).forEach(item => {
          if (this.nodeSet[item].id === cell.model.id) {
            this.curNode = this.items.filter(ele => ele.id === parseInt(item))[0]
            // 判断当前点击元素是事件还是字段
            if (cell.model.attributes.type === 'standard.Ellipse') {
              this.doEvent('edit', this.curNode)
            } else {
              this.doCondition('edit', this.curNode)
            }
          }
        })
      })
    },
    render() {
      const {nodes, links, nodeSet} = this

      nodes.forEach((node, i) => {
        const option = {
          position: {
            x: 60 + (100 + 50) * i,
            y: 60
          },
          size: {
            width: 100,
            height: 40
          },
          attrs: {
            body: {
              fill: '#ddd',
              strokeWidth: 1,
              stroke: '#ccc'
            },
            label: {
              text: node.name,
              fill: '#333'
            }
          }
        }
        let item
        if (node.type === 'start' || node.type === 'end') {
          item = new shapes.standard.Ellipse(option)
        } else {
          item = new shapes.standard.Rectangle(option)
        }
        item.addTo(this.graph)

        nodeSet[node.id] = item
      })

      links.forEach(([from, to]) => {
        const link = new shapes.standard.Link({
          source: nodeSet[from],
          target: nodeSet[to],
          attrs: {
            line: {
              stroke: '#aaa'
            }
          }
        })
        link.addTo(this.graph)
      })
      this.doLayout()
      const canvasRect = this.$refs.canvas.getClientRects()[0]
      // 扩大或缩小纸张以适应其中的内容（通常用于画布自适应浏览器大小）
      this.paper.fitToContent({ padding: 10, minWidth: canvasRect.width, minHeigth: canvasRect.height })
    },
    doLayout() {
      // https://www.cnblogs.com/fyjz/p/14303003.html
      layout.DirectedGraph.layout(this.graph, {
        dagre: dagre,
        graphlib: graphlib,
        setLinkVertices: false,
        rankDir: 'LR',
        align: 'DL',
        rankSep: 50,
        marginX: 60,
        marginY: 60,
        nodeSep: 50,
        edgeSep: 20
      })
    }
  },
  computed: {
    nodes() {
      const nodes = []

      this.items.forEach(item => {
        nodes.push(item)
      })

      return nodes
    },
    links() {
      const links = []

      this.items.forEach(item => {
        item.next.forEach(p => {
          links.push([item.id, p])
        })
      })

      return links
    }
  }
}
</script>

<style scoped lang="less">
.main-content {
  width: 100%;
  height: 100%;
  box-sizing: border-box;
  display: grid;
  grid-template-columns: 1.5fr 1fr;
  gap: 10px;
  .main-left {
    padding: 20px 10px 20px 20px;
    box-sizing: border-box;
    height: 100%;
    overflow: auto;
    .canvas {
      width: 100%;
      height: 100%;
      z-index: 1;
    }
    /deep/ .joint-cell {
      cursor: pointer;

      &:hover {
        [joint-selector="body"] {
          stroke: #666;
        }
      }

      &:active {
        text {
          fill: #00f;
        }
      }
    }
    .event-wrap {
      height: auto;
      .event-box {
        margin-bottom: 20px;
        display: flex;
        align-items: center;
        .event-item {
          border: 1px solid #1c8bf7;
          color: #1c8bf7;
          padding: 5px 10px;
          cursor: pointer;
          display: inline-block;
        }
        .condition-box {
          margin: 0 20px 0 20px;
          .condition-item {
            border: 1px solid #d0d0d0;
            padding: 5px 10px;
            cursor: pointer;
            margin-right: 10px;
            display: inline-block;
          }
          .condition-box-or {
            margin: 20px 0;
          }
        }
      }
    }
  }
  .main-right {
    padding: 20px 20px 20px 10px;
    box-sizing: border-box;
    height: 100%;
    overflow: auto;
    .pannel-opt {
      padding-bottom: 20px;
    }
    .pannel-event {
      box-shadow: 2px 2px 8px #d0d0d0;
      margin-bottom: 20px;
      .pannel-title {
        border-bottom: 1px solid #89c4ff;
        padding: 8px 10px;
        display: flex;
        align-items: center;
        justify-content: space-between;
      }
      .pannel-form {
        padding: 20px 20px 5px 10px;
      }
    }
  }
}
</style>
